package controlFlowGraph;

import java.util.HashSet;
import java.util.Set;

import domains.Interval;
import domains.IntervalBound;
import domains.IntervalDomain;

import parser.Token;
import parser.TokenType;

public class BinaryOp extends Expression {

	private Expression loperand;
	
	private Expression roperand;
	
	private Token operator;
	
	public BinaryOp(Token operator, Expression lop, Expression rop) {
		this.operator = operator;
		this.loperand = lop;
		this.roperand = rop;
	}

	public Expression getLoperand() {
		return loperand;
	}

	public Expression getRoperand() {
		return roperand;
	}

	public Token getOperator() {
		return operator;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof BinaryOp) {
			BinaryOp bin = (BinaryOp) obj;
			return (bin.getLoperand().equals(this.getLoperand())) &&
				(bin.getRoperand().equals(this.getRoperand())) &&
				(bin.getOperator().equals(this.getOperator()));
		}
		return false;
	}

	@Override
	public int hashCode() {
		return this.loperand.hashCode() + this.roperand.hashCode();
	}

	@Override
	public String toString() {
		return this.loperand.toString() + " " + this.operator.toString() + " " + this.roperand.toString();
	}

	@Override
	public Set<Variable> getInvolvedVariables() {
		Set<Variable> res = new HashSet<Variable>();
		res.addAll(this.loperand.getInvolvedVariables());
		res.addAll(this.roperand.getInvolvedVariables());
		return res;
	}
	
	@Override
	public Interval abstractExpressionEvaluation(IntervalDomain dom){
		Interval lop = this.loperand.abstractExpressionEvaluation(dom);
		Interval rop = this.roperand.abstractExpressionEvaluation(dom);
		
		Interval exp;
		switch(this.operator.getType()){
			case MUL: 
				exp = lop.multiply(rop);
				break;
			case DIVORSLASH:
				exp = lop.divide(rop);
				break;
			case PLUS:
				exp = lop.add(rop);
				break;
			case MINUS:
				exp = lop.subtract(rop);
				break;
			case LT:
				exp = lop.lt(rop);
				break;
			case LE:
				exp = lop.le(rop);
				break;
			case GT:
				exp = lop.gt(rop);
				break;
			case GE:
				exp = lop.ge(rop);
				break;
			case EQ:
				exp = lop.eq(rop);
				break;
			case NE:
				exp = lop.ne(rop);
				break;
			default:
				System.out.println("Unknown binary operator!");
				IntervalBound left = new IntervalBound(null,true,false);
				IntervalBound right = new IntervalBound(null,false,true);
				exp = new Interval(left,right);
				break;
		}
		return exp;
	}
	
	@Override
	public void replaceVariable(Variable v, Constant c){
		if(this.loperand instanceof Variable){
			if(((Variable) this.loperand).equals(v)){
				this.loperand = c;
			}
		} else {
			this.loperand.replaceVariable(v, c);
		}
		if(this.roperand instanceof Variable){
			if(((Variable) this.roperand).equals(v)){
				this.roperand = c;
			}
		} else {
			this.roperand.replaceVariable(v, c);
		}
	}
}
